﻿using ATM.Model;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace ATM.Components
{
    /// <summary>
    /// Cash dispenser is responsible for ejecting money from ATM.
    /// </summary>
    class CashDispenser
    {

        public CashDispenserDb Db { get; private set; }

        public CashDispenser()
        {
            Db = new CashDispenserDb();
        }

        /// <summary>
        /// Checks if cash dispenser contains enough bills.
        /// </summary>
        /// <param name="amount">Amount to check</param>
        /// <returns>true if there are enough bills</returns>
        [Pure]
        internal bool HasEnoughCash(int amount)
        {
            Contract.Requires(Db != null);
            Contract.Requires(Db.trays != null);
            Contract.Requires(Db.trays.Items != null);
            Contract.Requires(Db.trays.Items.Length > 0);
            Contract.Requires(amount > 0);


            int amountLeft = amount;
            Tray[] trays = Db.AvailableBills();

            trays = getWorkingCopy(trays);

            foreach (Tray tray in trays)
            {
                while (tray.BillCount > 0 && amountLeft >= tray.BillDenomination)
                {
                    amountLeft -= tray.BillDenomination;
                    tray.BillCount -= 1;
                }
            }

            return amountLeft == 0;
        }

        private Tray[] getWorkingCopy(Tray[] trays)
        {
            Contract.Requires(trays != null);
            List<Tray> tempList = new List<Tray>();

            foreach (Tray tray in trays)
            {
                Tray tempTray = new Tray();
                tempTray.BillCount = tray.BillCount;
                tempTray.BillDenomination = tray.BillDenomination;
                tempList.Add(tempTray);
            }

            return tempList.ToArray();
        }

        /// <summary>
        /// Ejects amount from ATM.
        /// 
        /// We assume that money will always be ejected if there is enough of it in the ATM
        /// </summary>
        /// <param name="amount">Amount to eject</param>
        internal void ejectAmount(int amount)
        {
            Contract.Requires(Db != null);
            Contract.Requires(Db.trays != null);
            Contract.Requires(Db.trays.Items != null);
            Contract.Requires(Db.trays.Items.Length > 0);
            Contract.Requires(HasEnoughCash(amount));

            int amountLeft = amount;
            Tray[] trays = Db.AvailableBills();
            List<int> usedBills = new List<int>();

            for (int i = 0; i < trays.Length; i++)
            {
                for (int j = i; j < trays.Length; j++)
                {
                    Tray tray = trays[j];
                    while (tray.BillCount > 0 && amountLeft > tray.BillDenomination)
                    {
                        amountLeft -= tray.BillDenomination;
                        usedBills.Add(tray.BillDenomination);
                        tray.BillCount--;
                    }
                }
                if (amountLeft == 0)
                    break;
                amountLeft = amount;
                usedBills = new List<int>();
            }
        }
    }
}
